package util

import (
	"encoding/json"
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"log"
	"time"
)

// MQTTClient MQTT客户端
type MQTTClient struct {
	client mqtt.Client
}

// MQTT消息结构
type CabinetCommand struct {
	Action      string `json:"action"`       // 动作：open-开柜，close-关柜
	CabinetNo   string `json:"cabinet_no"`   // 柜子编号
	Timestamp   int64  `json:"timestamp"`    // 时间戳
}

type CabinetStatus struct {
	CabinetNo   string `json:"cabinet_no"`   // 柜子编号
	Status      string `json:"status"`       // 状态：opened-已打开，closed-已关闭，error-错误
	Timestamp   int64  `json:"timestamp"`    // 时间戳
	ErrorMsg    string `json:"error_msg"`    // 错误信息（如果有）
}

const (
	// MQTT主题
	TopicCabinetControl = "cabinet/control/%s" // %s 为柜子编号
	TopicCabinetStatus  = "cabinet/status/%s"  // %s 为柜子编号
	TopicCabinetError   = "cabinet/error/%s"   // %s 为柜子编号
)

// NewMQTTClient 创建新的MQTT客户端
func NewMQTTClient(broker string, clientID string) (*MQTTClient, error) {
	opts := mqtt.NewClientOptions()
	opts.AddBroker(broker)
	opts.SetClientID(clientID)
	opts.SetKeepAlive(60 * time.Second)
	opts.SetPingTimeout(1 * time.Second)
	opts.SetAutoReconnect(true)
	opts.SetConnectionLostHandler(func(client mqtt.Client, err error) {
		log.Printf("Connection lost: %v", err)
	})

	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		return nil, token.Error()
	}

	return &MQTTClient{
		client: client,
	}, nil
}

// SendOpenCommand 发送开柜命令
func (m *MQTTClient) SendOpenCommand(cabinetNumber string) error {
	command := CabinetCommand{
		Action:    "open",
		CabinetNo: cabinetNumber,
		Timestamp: time.Now().Unix(),
	}

	payload, err := json.Marshal(command)
	if err != nil {
		return fmt.Errorf("failed to marshal command: %v", err)
	}

	topic := fmt.Sprintf(TopicCabinetControl, cabinetNumber)
	token := m.client.Publish(topic, 0, false, payload)
	if token.Wait() && token.Error() != nil {
		return token.Error()
	}

	// 订阅状态反馈
	statusTopic := fmt.Sprintf(TopicCabinetStatus, cabinetNumber)
	token = m.client.Subscribe(statusTopic, 0, func(client mqtt.Client, msg mqtt.Message) {
		var status CabinetStatus
		if err := json.Unmarshal(msg.Payload(), &status); err != nil {
			log.Printf("Failed to unmarshal status: %v", err)
			return
		}

		if status.Status == "error" {
			log.Printf("Cabinet %s error: %s", cabinetNumber, status.ErrorMsg)
		} else if status.Status == "opened" {
			log.Printf("Cabinet %s opened successfully", cabinetNumber)
		}
	})

	if token.Wait() && token.Error() != nil {
		return token.Error()
	}

	return nil
}

// SendCloseCommand 发送关柜命令
func (m *MQTTClient) SendCloseCommand(cabinetNumber string) error {
	command := CabinetCommand{
		Action:    "close",
		CabinetNo: cabinetNumber,
		Timestamp: time.Now().Unix(),
	}

	payload, err := json.Marshal(command)
	if err != nil {
		return fmt.Errorf("failed to marshal command: %v", err)
	}

	topic := fmt.Sprintf(TopicCabinetControl, cabinetNumber)
	token := m.client.Publish(topic, 0, false, payload)
	if token.Wait() && token.Error() != nil {
		return token.Error()
	}

	// 订阅状态反馈
	statusTopic := fmt.Sprintf(TopicCabinetStatus, cabinetNumber)
	token = m.client.Subscribe(statusTopic, 0, func(client mqtt.Client, msg mqtt.Message) {
		var status CabinetStatus
		if err := json.Unmarshal(msg.Payload(), &status); err != nil {
			log.Printf("Failed to unmarshal status: %v", err)
			return
		}

		if status.Status == "error" {
			log.Printf("Cabinet %s error: %s", cabinetNumber, status.ErrorMsg)
		} else if status.Status == "closed" {
			log.Printf("Cabinet %s closed successfully", cabinetNumber)
		}
	})

	if token.Wait() && token.Error() != nil {
		return token.Error()
	}

	return nil
}

// SubscribeCabinetStatus 订阅柜子状态
func (m *MQTTClient) SubscribeCabinetStatus(cabinetNumber string, callback func(status CabinetStatus)) error {
	topic := fmt.Sprintf(TopicCabinetStatus, cabinetNumber)
	token := m.client.Subscribe(topic, 0, func(client mqtt.Client, msg mqtt.Message) {
		var status CabinetStatus
		if err := json.Unmarshal(msg.Payload(), &status); err != nil {
			log.Printf("Failed to unmarshal status: %v", err)
			return
		}
		callback(status)
	})

	if token.Wait() && token.Error() != nil {
		return token.Error()
	}

	return nil
}

// SubscribeCabinetError 订阅柜子错误信息
func (m *MQTTClient) SubscribeCabinetError(cabinetNumber string, callback func(errorMsg string)) error {
	topic := fmt.Sprintf(TopicCabinetError, cabinetNumber)
	token := m.client.Subscribe(topic, 0, func(client mqtt.Client, msg mqtt.Message) {
		callback(string(msg.Payload()))
	})

	if token.Wait() && token.Error() != nil {
		return token.Error()
	}

	return nil
}

// Close 关闭MQTT连接
func (m *MQTTClient) Close() {
	m.client.Disconnect(250)
}
